// Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <memory>
#include <string>
#include "shell/browser/ui/file_dialog.h"

#include "base/files/file_util.h"
#include "base/functional/callback.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/task/thread_pool.h"
#include "filemanagement/file_uri/oh_file_uri.h"
#include "ohos/adapter/dialog/dialog_adapter.h"
#include "shell/browser/native_window_views.h"
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_converters/file_path_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/promise.h"


namespace file_dialog {

DialogSettings::DialogSettings() = default;
DialogSettings::DialogSettings(const DialogSettings&) = default;
DialogSettings::~DialogSettings() = default;

namespace {

void ConvertFilters(const Filters& filters, base::Value::List& filters_list) {
  for (auto& filter : filters) {
    base::Value::Dict file_filter;
    base::Value::List extensions_list;
    for (auto& extension : filter.second) {
      extensions_list.Append(extension);
    }
    file_filter.Set("name", filter.first);
    file_filter.Set("extensions", std::move(extensions_list));
    filters_list.Append(std::move(file_filter));
  }
}

std::string SettingsConvertToJsonStr(const DialogSettings& settings) {
  int parent_id = -1;
  electron::NativeWindow* parent_window = settings.parent_window;
  if (parent_window) {
    parent_id = parent_window->GetAcceleratedWidget();
  }
  std::string title = settings.title;
  std::string message = settings.message;
  std::string button_label = settings.button_label;
  std::string default_path = settings.default_path.AsUTF8Unsafe();
  base::Value::List filters;
  ConvertFilters(settings.filters, filters);
  bool is_open_directory = settings.properties & OPEN_DIALOG_OPEN_DIRECTORY;
  bool is_open_mixed =
      (settings.properties & OPEN_DIALOG_OPEN_FILE) && is_open_directory;
  bool is_multi_selections = settings.properties & OPEN_DIALOG_MULTI_SELECTIONS;

  base::Value::Dict settings_dict;
  settings_dict.Set("parent_id", parent_id);
  settings_dict.Set("title", title);
  settings_dict.Set("message", message);
  settings_dict.Set("button_label", button_label);
  settings_dict.Set("default_path", default_path);
  settings_dict.Set("filters", std::move(filters));
  settings_dict.Set("properties_open_directory", is_open_directory);
  settings_dict.Set("properties_open_mixed", is_open_mixed);
  settings_dict.Set("properties_multi_selections", is_multi_selections);

  std::string settings_json;
  base::JSONWriter::Write(settings_dict, &settings_json);
  return settings_json;
}

ohos::adapter::dialog::OpenDialogResult ShowOpenDialogAdapter(
    const DialogSettings& settings) {
  auto result =
      ohos::adapter::dialog::DialogAdapter::GetInstance().ShowOpenDialog(
          SettingsConvertToJsonStr(settings));
  for (int index = 0; index < static_cast<int>(result.file_paths.size()); index++) {
    char* path = nullptr;
    auto code =
        OH_FileUri_GetPathFromUri(result.file_paths[index].c_str(),
                                  result.file_paths[index].length(), &path);
    if (code == FileManagement_ErrCode::ERR_OK) {
      result.file_paths[index] = path;
    } else {
      LOG(ERROR) << __func__ << " error OH_FileUri_GetPathFromUri failed";
    }
  }
  return result;
}

ohos::adapter::dialog::SaveDialogResult ShowSaveDialogAdapter(
    const DialogSettings& settings) {
  auto result =
      ohos::adapter::dialog::DialogAdapter::GetInstance().ShowSaveDialog(
          SettingsConvertToJsonStr(settings));
  char* path = nullptr;
  auto code =
      OH_FileUri_GetPathFromUri(result.file_path.c_str(),
                                result.file_path.length(), &path);
  if (code == FileManagement_ErrCode::ERR_OK) {
    result.file_path = path;
  } else {
    LOG(ERROR) << __func__ << " error OH_FileUri_GetPathFromUri failed";
  }
  return result;
}

}  // namespace

bool ShowOpenDialogSync(const DialogSettings& settings,
                        std::vector<base::FilePath>* paths) {
  auto result = ShowOpenDialogAdapter(settings);
  if (result.canceled) {
    return false;
  }
  for (auto& path : result.file_paths) {
    paths->emplace_back(base::FilePath(path));
  }
  return true;
}

void ShowOpenDialog(const DialogSettings& settings,
                    gin_helper::Promise<gin_helper::Dictionary> promise) {
  auto done = [](gin_helper::Promise<gin_helper::Dictionary> promise,
                ohos::adapter::dialog::OpenDialogResult result) {
    v8::HandleScope handle_scope(promise.isolate());
    gin::Dictionary dict = gin::Dictionary::CreateEmpty(promise.isolate());
    dict.Set("canceled", result.canceled);
    dict.Set("filePaths", result.file_paths);
    promise.Resolve(dict);
  };

  base::ThreadPool::PostTaskAndReplyWithResult(
      FROM_HERE, {base::TaskPriority::BEST_EFFORT},
      base::BindOnce(&ShowOpenDialogAdapter, settings),
      base::BindOnce(done, std::move(promise)));
}

bool ShowSaveDialogSync(const DialogSettings& settings, base::FilePath* path) {
  auto result = ShowSaveDialogAdapter(settings);
  if (result.canceled) {
    return false;
  }
  *path = base::FilePath(result.file_path);
  return true;
}

void ShowSaveDialog(const DialogSettings& settings,
                    gin_helper::Promise<gin_helper::Dictionary> promise) {
  auto done = [](gin_helper::Promise<gin_helper::Dictionary> promise,
                ohos::adapter::dialog::SaveDialogResult result) {
    v8::HandleScope handle_scope(promise.isolate());
    gin::Dictionary dict = gin::Dictionary::CreateEmpty(promise.isolate());
    dict.Set("canceled", result.canceled);
    dict.Set("filePath", result.file_path);
    promise.Resolve(dict);
  };

  base::ThreadPool::PostTaskAndReplyWithResult(
      FROM_HERE, {base::TaskPriority::BEST_EFFORT},
      base::BindOnce(&ShowSaveDialogAdapter, settings),
      base::BindOnce(done, std::move(promise)));
}

}  // namespace file_dialog
